/*
 * SelectInterrupter.cpp
 *
 *  Created on: Jun 29, 2016
 *      Author: Lucifer
 */

#include "SelectInterrupter.h"

namespace BFX {

SelectInterrupter::SelectInterrupter() {
	// Create a accept socket on local port 0.
	REF<InetAddress> listenAddress = new InetAddress(0, true, InetAddress::AF_IPv4);
	REF<Socket> as = new Socket(listenAddress);
	BFX_ASSERT(as->isValid());
	if (!as->isValid())
		return;
	as->setReuseAddress(true);
	as->listen();

	// Create a client socket that connect to local port 0.
	REF<InetAddress> connectAddr = new InetAddress();
	as->getSockName(connectAddr);
	// Some broken firewalls on Windows will cause getsockname to return 0.0.0.0
	// when the socket is actually bound to 127.0.0.1.
	((sockaddr_in*) connectAddr->getSockAddr())->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
	as->listen();

	REF<Socket> cs = new Socket();
	cs->connect(connectAddr);
	BFX_ASSERT(cs->isValid());
	if (!cs->isValid())
		return;

	// Create a server socket that attach to the client socket.
	REF<Socket> ss = new Socket();
	as->accept(ss, nullptr);
	BFX_ASSERT(ss->isValid());
	if (!ss->isValid())
		return;

	// Sets both client/server sockets to non-blocking and no-delay mode.
	cs->setNonBlocking(true);
	cs->setTcpNoDelay(true);
	ss->setNonBlocking(true);
	ss->setTcpNoDelay(true);

	_reader = ss;
	_writer = cs;
}

SelectInterrupter::~SelectInterrupter() {
	_reader = nullptr;
	_writer = nullptr;
}

bool SelectInterrupter::interrupt() {
	BFX_ASSERT(isValid());
	if (!isValid())
		return false;

	char byte = 0;
	_writer->send(&byte, 1);
}

bool SelectInterrupter::reset() {
	BFX_ASSERT(isValid());
	if (!isValid())
		return false;

	const int BUF_SIZE = 1024;
	char buf[BUF_SIZE];
	int bytesRecv = _reader->receive(buf, BUF_SIZE);
	bool wasInterrupted = (bytesRecv > 0);
	while (bytesRecv == BUF_SIZE)
		bytesRecv = _reader->receive(buf, BUF_SIZE);
	return wasInterrupted;
}

fildes_t SelectInterrupter::getReadDescriptor() {
	BFX_ASSERT(isValid());
	if (!isValid())
		return INVALID_FILDES;

	return _reader->getDescriptor();
}

} /* namespace BFX */
